#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>

// NETWORK
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

// New includes
#include "util.h"
#include "network.h"

/*
 * THIS IS THE ROUTETRACE
 * the routetrace application uses a TTL flag to find the shortest route to the
 * destination port & hostname
 *
 * The RECEIVER is initialized using the following command:
 *
 * requester -p <port> -o <file option>
 */

// CONFIGRATION VARIABLES
#define NUM_ROUTETRACE_ARGS 6

int rt_port;          // Port on which traceroute waits for packets
char *src_hostname;
int src_port;
char *dest_hostname;
int dest_port;
int debug;

void dg_recv(int sockfd, struct sockaddr_in*pcliaddr, socklen_t clilen)
{

    int ttl = 0;
    struct hostent *src_host = gethostbyname(src_hostname);



    // SET UP DESTINATION INFORMATION
    src_host = gethostbyname(src_hostname);
    bzero(pcliaddr, sizeof(*pcliaddr));
    pcliaddr->sin_family = AF_INET;
    pcliaddr->sin_port = htons(src_port);
    bcopy((char *)src_host->h_addr,
    (char *)&(pcliaddr->sin_addr.s_addr), src_host->h_length);

    // CAST BUFFERS
    rt_packet = (RT_Packet*)rt_packet_buf;
    packet = (Packet*)packet_buf;
    em_packet = (Em_Packet*)em_packet_buf;

    // LOAD BUFFERS
    struct hostent *h;

    char hostname[1024];
    hostname[1023] = '\0';
    gethostname(hostname, 1023);
    h = gethostbyname(hostname);

    load_rt_packet(rt_packet, 'T', ttl, src_hostname, src_port,
    			dest_hostname, dest_port);

    load_packet(packet, 'T', 0, sizeof(RT_Packet), (char*) rt_packet);

    load_em_packet(em_packet, 1, h->h_addr, rt_port, "wut",
                 0, sizeof(Packet) + strlen(packet->payload), packet);

    // SEND THE TRACEROUTE PACKET
    int n;
    if ((n = sendto(sockfd, em_packet_buf,
                        sizeof(Em_Packet) + em_packet->length,
                        0,(struct sockaddr*) pcliaddr, clilen)) == -1)
    {
            perror(ERROR_SENDTO);
	    exit(1);
    }

    // PRINT WHAT WAS SENT (to verify contents)
    print_em_packet((Em_Packet*)em_packet_buf);
    print_sent_packet((Packet*)((Em_Packet*)em_packet_buf)->payload, pcliaddr, n);

    while(1) 
    {
        // START RECEIVE ACTION
        if ((n = recvfrom(sockfd, em_packet_buf,
                      MAX_PACKET_SIZE, 0,
                     (struct sockaddr*)pcliaddr, &clilen)) == -1)
        {
            perror(ERROR_RECVFROM);
	    exit(1);
        }
        print_time();

        em_packet_buf[n] = '\0'; // Null terminate

	// CAST THE NEW PACKET
        em_packet = (Em_Packet*)em_packet_buf;
        packet = (Packet*)em_packet->payload;
        rt_packet = (RT_Packet*) packet->payload;

	// PRINT THE RECEIVED PACKET
	print_em_packet(em_packet);

	if(rt_packet->dest_port == dest_port){
	    exit(1);
	}

	ttl++;

        // SET UP DESTINATION INFORMATION
        src_host = gethostbyname(src_hostname);
        bzero(pcliaddr, sizeof(*pcliaddr));
        pcliaddr->sin_family = AF_INET;
        pcliaddr->sin_port = htons(src_port);
        bcopy((char *)src_host->h_addr,
        (char *)&(pcliaddr->sin_addr.s_addr), src_host->h_length);

        // LOAD BUFFERS

        load_rt_packet(rt_packet, 'T', ttl, src_hostname, src_port,
			dest_hostname, dest_port);

    	load_packet(packet, 'T', 0, sizeof(RT_Packet), (char*) rt_packet);

        load_em_packet(em_packet, 1, h->h_name, rt_port, "wut",
                 0, sizeof(Packet) + strlen(packet->payload), packet);

        int n;
	print_time();
		
	// Send the new TTL packet
	if ((n = sendto(sockfd, em_packet_buf,
                        sizeof(Em_Packet) + em_packet->length,
                        0,(struct sockaddr*) pcliaddr, clilen)) == -1)
	{
            		perror(ERROR_SENDTO);
	    		exit(1);
	}

	// Print what was sent
	print_em_packet((Em_Packet*)em_packet_buf);
	print_sent_packet((Packet*)((Em_Packet*)em_packet_buf)->payload, pcliaddr, n);

	// Reset the cliaddr
        src_host = gethostbyname(src_hostname);
        bzero(pcliaddr, sizeof(*pcliaddr));
        pcliaddr->sin_family = AF_INET;
        pcliaddr->sin_port = htons(src_port);
        bcopy((char *)src_host->h_addr,
        (char *)&(pcliaddr->sin_addr.s_addr), src_host->h_length);
    }
}
	    




int main(int argc, char *argv[])
{
    int sockfd;
    int parse_code;

    struct sockaddr_in servaddr;
    struct sockaddr_in cliaddr;
    struct hostent *server;

    // Verify number of command line arguments
    if (argc != NUM_ROUTETRACE_ARGS)
    {
        perror(ERROR_REQUESTER_USAGE);
        exit(1);
    }

    // Parse command line information and set up configurations
    while ((parse_code = getopt(argc, argv, "pfhow:")) != -1)
    {
        switch (parse_code)
        {
            case 'a':
	        rt_port = strtol(argv[optind], NULL, 10);
		if (rt_port <= 1024 || rt_port >= 65536)
                {
		    perror(ERROR_PORT_RANGE);
		    exit(1);
                }
	        break;
            case 'b':
	        src_hostname = argv[optind];
		break;
            case 'c':
	        src_port = strtol(argv[optind], NULL, 10);
		if (src_port <= 1024 || src_port >= 65536)
		{
		    perror(ERROR_PORT_RANGE);
		    exit(1);
		}
		break;
	    case 'd':
	        dest_hostname = argv[optind];
		break;
	    case 'e':
	        dest_port = strtol(argv[optind], NULL, 10);
                if (dest_port <= 1024 || dest_port >= 65536)
		{
                    perror(ERROR_PORT_RANGE);
		    exit(1);
		}
	        break;
	    case 'f':
                debug = strtol(optarg, NULL,10);
                break;
	    default:
               perror(ERROR_RT_USAGE);
	       exit(1);
	}
    }

//printf ("\033[34mROUTETRACE SETUP WITH THE FOLLOWING CONFIG:\033[1;34m\n> in_port:\t%d\n> out_port:\t%d\n> file_opt:\t%s\033[0m\n\n", in_port, f_port, file_option);


    // Create socket for traceroute
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror(ERROR_SOCKET);
        exit(1);
    }

    char hostname[1024];
    hostname[1023] = '\0';

    // Setup routetrace's server addr
    gethostname(hostname, sizeof(hostname));
    server = gethostbyname(hostname);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(rt_port);
    bcopy((char *)server->h_addr,
        (char *)&servaddr.sin_addr.s_addr, server->h_length);

    if (bind(sockfd, (struct sockaddr *) &servaddr,
        sizeof(servaddr)) < 0)
    {
        perror(ERROR_BIND);
        exit(1);
    }

    // Do actual traceroute work
    dg_recv(sockfd, &cliaddr, sizeof(cliaddr));

    exit(0);
}
